/**
 * 
 */
package RecordManagement;

import java.io.*;
import java.util.*;

import PagedFile.*;

/**
 * @author Neil
 *
 */
public class RM_Manager implements RM_Manager_Interface {

	PF_Manager pfm;
	public final static int RM_PAGEHEAD_SIZE = 4;
	private Map<String, RM_FileHandle> openFiles = new HashMap<String, RM_FileHandle>();
	
	public RM_Manager (PF_Manager pfm) {
		this.pfm = pfm;
	}
	/* (non-Javadoc)
	 * @see RecordManagement.RM_Manager_Interface#CloseFile(RecordManagement.RM_FileHandle)
	 */
	@Override
	public void CloseFile(RM_FileHandle fh) throws PF_Abort_Exception {
		if (fh.openCnt==0)
			throw new RM_Abort_Exception("Handle Not Open");
		--fh.openCnt;
		
		// Store the record head into the file
		Buffer headBuffer = fh.pfFileHandle.GetThisPage(1);
		headBuffer.mark(this.hashCode());
		fh.rmFileHead.put_into_ByteBuffer(headBuffer.contents);
		headBuffer.write();
		fh.pfFileHandle.UnpinPage(1);
		
		if (fh.openCnt==0 && openFiles.get(fh.pfFileHandle.filename)!= fh)
			throw new RM_Abort_Exception("Fatal");
		openFiles.remove(fh.pfFileHandle.filename);
		try {
			pfm.CloseFile(fh.pfFileHandle);
		} catch (PF_Abort_Exception pfe) {
			throw new RM_Abort_Exception(pfe.getMessage());
		}
	}

	/* (non-Javadoc)
	 * @see RecordManagement.RM_Manager_Interface#CreateFile(java.lang.String, int)
	 */
	@Override
	public RM_FileHandle CreateFile(String filename, int recordSize) throws IOException {
		
		//Determine the number of records per page
		double x = (double)(Buffer.BLOCK_SIZE-RM_PAGEHEAD_SIZE) - 1.0;
		double y = (double)recordSize + 0.125;
		int recordPerPage = (int)(x/y);
		if (recordPerPage < 1) 
			throw new RM_Abort_Exception("Record Size Too Big");
		
		PF_FileHandle pfh=null;
		try {
			pfh = pfm.CreateFile(filename);
		} catch (PF_Abort_Exception pfe) {
			throw new RM_Abort_Exception(pfe.getMessage());
		}
		/* Establish a new record file head, 
		 * and store it into the file */
		Buffer headBuffer = pfh.AllocatePage();
		if (headBuffer.block.pageNum!=1)
			throw new RM_Abort_Exception("Fatal");
		RM_FileHead rfhead = new RM_FileHead(0,
				recordSize,
				recordPerPage,
				RM_PAGEHEAD_SIZE+(recordPerPage+8)/8);
		headBuffer.mark(this.hashCode());
		rfhead.put_into_ByteBuffer(headBuffer.contents);
		headBuffer.write();
		pfh.UnpinPage(1);
		
		/* Establish a new RM_FileHandle */
		RM_FileHandle rfh = new RM_FileHandle(pfh, rfhead, pfm);
		openFiles.put(filename, rfh);
		
		return rfh;
	}

	/* (non-Javadoc)
	 * @see RecordManagement.RM_Manager_Interface#OpenFile(java.lang.String)
	 */
	@Override
	public RM_FileHandle OpenFile(String filename) throws IOException {
		RM_FileHandle rfh = openFiles.get(filename);
		if (rfh!=null) {
			rfh.openCnt++;
			return rfh;
		}
		// Notice Paged File Manager to open the file
		PF_FileHandle pfh = pfm.OpenFile(filename);
		
		// Get the record file head from the file
		Buffer headBuffer = pfh.GetThisPage(1);
		RM_FileHead rmFileHead = new RM_FileHead(headBuffer.contents);
		pfh.UnpinPage(1);
		
		rfh = new RM_FileHandle(pfh, rmFileHead, pfm);
		openFiles.put(filename, rfh);
		
		// Establish a new RM_FileHandle and return it
		return rfh;
	}

	/* (non-Javadoc)
	 * @see RecordManagement.RM_Manager_Interface#DestoryFile(java.lang.String)
	 */
	@Override
	public void DestoryFile(String filename) {
		if (openFiles.remove(filename)==null)
			throw new RM_Abort_Exception("File Not Open");
		try {
			pfm.DestroyFile(filename);
		} catch (PF_Abort_Exception pfe) {
			throw new RM_Abort_Exception(pfe.getMessage());
		}
	}

}
